package com.zkh.myframe.core;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import com.zkh.myframe.annotation.Interceptors;
import com.zkh.myframe.database.mapper.MapperEntity;
import com.zkh.myframe.database.mapper.parser.Parser;
import com.zkh.myframe.exception.FrameException;
import com.zkh.myframe.interceptor.Interceptor;
import com.zkh.myframe.interceptor.UploadFileInterceptor;
import com.zkh.myframe.listener.InitialListener;
import com.zkh.myframe.server.tomcat.websocket.WebSocket;
import com.zkh.myframe.server.tomcat.websocket.WebSocketBranch;
import com.zkh.myframe.server.tomcat.websocket.WebSocketCore;
import com.zkh.myutils.code.CoderUtils;
import com.zkh.myutils.io.log.Logger;
import com.zkh.myutils.scheduled.ScheduledTask;
import com.zkh.myutils.system.ConfigLoader;
import com.zkh.myutils.utils.Assert;
import com.zkh.myutils.utils.StringUtils;

/**
 * 框架配置对象
 */
public class FrameConfigure {
	//获取项目根目录
	private static String root;
	//系统配置
	private static Map<String, String> frameConfig;
	//控制器映射配置
	private static Map<String, Class<?>> controllerConfig = new HashMap<>();
	//Mapper映射
	private static Map<String, MapperEntity> mapperMap = new HashMap<>();
	//定时任务Bean
	private static Map<String, ScheduledTask> scheduledBeanMap = new HashMap<>();
	//请求链接
	private static List<String> urls = new ArrayList<>();
	//配置编码
	private static Charset globalCharset;
	//urlSuffix配置
	private static String[] urlSuffixes;
	//拦截器
	private static List<Class<? extends Interceptor>> interceptors = new ArrayList<>();
	//WebSocket
	private static List<Class<? extends WebSocket>> websockets = new ArrayList<>();
	//转发前后缀配置
	private static String[] preSuffix;
	//首页拦截
	private static String firstPage;
	//单例模式
	private static Boolean resourceSingleton;
	//执行包（内嵌tomcat项目正式发布时是一个jar包，该变量用于存放这个包对象）
	private static JarFile executePackage;
	
	//初始化容器
	static {
		//根目录，用来解析资源。try用来屏蔽jar包报错的问题
		try {
			root = CoderUtils.decodeURL(FrameConfigure.class.getClassLoader().getResource("").getPath());
		}catch(Exception e) {}
	}
	
	/**
	 * 设置执行包对象
	 * @param jarFile 执行包对象
	 */
	public static void setExecutePackage(JarFile jarFile) {
		executePackage = jarFile;
	}
	
	/**
	 * 加载系统配置
	 * @param pluginList 插件列表
	 */
	public static void loadFrameConfig(List<File> pluginList){
		//加载配置文件
		frameConfig = ConfigLoader.getMap("frame");
		//加载定时任务地址，如果不为空，则拼接扫描地址
		Assert.doIfNotEmpty(ConfigLoader.getString("scheduled.taskLocation", true), taskLocations->{
			//获取扫描地址并拼接
			frameConfig.compute("scanLocation", (k, scanLocation) -> (StringUtils.isEmpty(scanLocation) ? "" : scanLocation + ";") + taskLocations);
		});
		//遍历插件
		pluginList.forEach(plugin->{
			//插件名称
			String pluginName = Assert.getIfNotNull(plugin.getAbsolutePath(), path->path.substring(path.lastIndexOf(File.separator)+1));
			//日志
			Logger.printLogError("处理插件：{}", pluginName);
			//加载可执行包配置
			try {
				loadExecutePackageConfig(new JarFile(plugin));
			}catch (IOException e) {
				Logger.errors("插件加载失败：" + pluginName, e);
			}
		});
		Logger.printLogError("处理项目资源");
		//插件加载完毕，开始加载框架配置
		loadFrameConfig();
	}
	
	/**
	 * 加载系统配置
	 */
	private static void loadFrameConfig(){
		/** 加载公共配置 **/
		//加载urlSuffix配置
		loadUrlSuffix();
		//加载前后缀
		loadPreSuffix();
		//存在执行包，则扫描
		if(executePackage!=null) {
			//加载可执行包配置
			loadExecutePackageConfig(executePackage);
		}else {
			/** 依次加载配置 **/
			//扫描包下的Controller、拦截器、WebSocket、定时任务等
			scanPackage();
			//加载Mapper
			loadMapperFile();
		}
	}
	
	/**
	 * 分割配置项
	 * @param config 配置项
	 */
	private static List<String> splitConfig(String config){
		//配置项值
		List<String> configList = new ArrayList<>();
		//不为空
		if(!StringUtils.isEmpty(config)) {
			//分割地址并遍历
			for(String l: config.split(";")) {
				configList.add(l.endsWith("/") ? l : l + "/");
			}
		}
		return configList;
	}
	
	/**
	 * 加载可执行包配置
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static void loadExecutePackageConfig(JarFile executePackage) {
		//日志
		Logger.info("[{}] 开始加载资源及Mapper配置", InitialListener.getContextName());
		//时间
		long start = System.currentTimeMillis();
		//获取mapperResource配置
		String mapperConfig = frameConfig.get("mapperResource");
		//获取扫描地址
		String scanLocationConf = frameConfig.get("scanLocation");
		/** 处理mapper地址 **/
		//mapper地址
		List<String> mapperList = splitConfig(mapperConfig);
		//匹配Mapper的方法
		Function<String, Boolean> isMatchedMapper = filename->{
			//遍历配置mapper
			for(String mapper: mapperList) {
				//匹配到mapper
				if(filename.startsWith(mapper) && filename.endsWith("Mapper.xml")) {
					//返回
					return true;
				}
			}
			//未匹配到
			return false;
		};
		/** 处理扫描地址 **/
		//扫描地址
		List<String> scanLocationList = splitConfig(scanLocationConf);
		//匹配扫描地址的方法
		Function<String, int[]> matchLocation = filename->{
			//遍历配置路径
			for(String scanLocation: scanLocationList) {
				//匹配到配置
				if(filename.startsWith(scanLocation)) {
					//类型（1：控制器；2：拦截器；3：WebSocket；4：ScheduledTask）
					int type = filename.endsWith("Controller.class") ? 1 : (filename.endsWith("Interceptor.class") ? 2 : (filename.endsWith("WebSocket.class") ? 3 : (filename.endsWith("ScheduledTask.class") ? 4 : -1)));
					//返回
					return type==-1 ? null : new int[] {type, scanLocation.length()-1};
				}
			}
			//未匹配到
			return null;
		};
		//迭代器
		Enumeration<JarEntry> enumeration = executePackage.entries();
		//遍历
		while(enumeration.hasMoreElements()) {
			//当前目录或文件
			JarEntry entry = enumeration.nextElement();
			//当前文件
			String filePath = entry.getName();
			//匹配路径
			int[] matchRes = matchLocation.apply(filePath);
			//匹配到
			if(matchRes!=null) {
				//结果类型和前缀长度
				int type = matchRes[0], preLen = matchRes[1];
				//完整类名
				String fullClassName = filePath.substring(0, filePath.length()-6).replace('/', '.');
				try {
					//获取Class对象
					Class clazz = Class.forName(fullClassName);
					//控制器
					if(type==1) {
						//前缀长度和Class前的/索引
						int sep = filePath.lastIndexOf('/');
						//获取key
						String key = filePath.substring(preLen, sep + 1) + Character.toLowerCase(filePath.charAt(sep + 1)) + filePath.substring(sep + 2, filePath.length()-16);
						//将Controller放入Map容器中
						controllerConfig.put(key, clazz);
						urls.add(key);
					}
					//如果是拦截器，则添加到集合
					else if(type==2 && Interceptor.class.isAssignableFrom(clazz)) {
						interceptors.add(clazz);
					}
					//如果是WebSocket，则添加到集合
					else if(type==3 && WebSocket.class.isAssignableFrom(clazz)) {
						websockets.add(clazz);
					}
					//如果是ScheduledTask，则添加到集合
					else if(type==4 && ScheduledTask.class.isAssignableFrom(clazz)) {
						scheduledBeanMap.put(clazz.getSimpleName(), (ScheduledTask) clazz.newInstance());
					}
				}catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
					//任务Bean
					if(type==4) {
						//创建任务失败，输出日志即可
						Logger.error("任务Bean创建失败：Bean名称：{}, 错误原因：{}", fullClassName, e.getMessage());
					}else {
						//输出异常，但不用处理
						Logger.error(e);
					}
				}
			}//匹配Mapper
			else if(isMatchedMapper.apply(filePath)) {
				//获取文件流
				try(InputStream inputStream = executePackage.getInputStream(entry)) {
					//文件名
					String filename = filePath.substring(filePath.lastIndexOf('/') + 1);
					//解析
					MapperParser.parseMapper(inputStream, filename);
				}catch (IOException e) {
					throw new FrameException(e);
				}
			}
		}
		//urls排序，长的在前
		urls.sort((a, b)->b.length()-a.length());
		//加载完成，处理后置操作
		dealInterceptor();
		dealWebsocket();
		//检查未处理的SQL
		Parser.checkUnDealSQL();
		//执行后的时间
		long end = System.currentTimeMillis();
		//计算执行时间
		Logger.info("[{}] 资源及Mapper配置加载完毕，共消耗 {} ms", InitialListener.getContextName(), end-start);

	}
	
	/**
	 * 根据键名获取配置
	 */
	public static String getValue(String key){
		//如果尚未加载系统配置，则加载系统配置
		if(frameConfig==null){
			loadFrameConfig();
		}
		//返回配置信息
		return frameConfig.get(key);
	}
	
	/**
	 * 获取全局编码
	 */
	public static Charset getGlobalCharset() {
		//没有编码
		if(globalCharset==null) {
			//读取编码配置
			String cs = getValue("charset");
			//为空，默认UTF8
			if(StringUtils.isEmpty(cs)) {
				cs = "utf-8";
			}
			//编码对象
			globalCharset = Charset.forName(cs);
		}
		//返回编码
		return globalCharset;
	}
	
	/**
	 * 获取单例模式
	 */
	public static boolean getResourceSingleton() {
		//为空
		if(resourceSingleton==null) {
			//获取配置
			String t = getValue("resourceSingleton");
			//判断值
			resourceSingleton = StringUtils.isEmpty(t) || "true".equals(t);
		}
		return resourceSingleton;
	}
	
	/**
	 * 获取基础异常页面配置
	 */
	public static String getBaseExceptionPage() {
		return getValue("baseExceptionPage");
	}
	
	/**
	 * 获取首页拦截配置
	 */
	public static String getFirstPage(){
		//尚未加载
		if(firstPage==null){
			//获取配置
			firstPage = getValue("firstPage");
			//检查
			if(firstPage==null || firstPage.trim().isEmpty()){
				firstPage = "";
			}
		}
		//返回
		return firstPage;
	}

	/**
	 * 获取前缀
	 */
	public static String getPrefix(){
		return preSuffix[0];
	}
	
	/**
	 * 获取后缀
	 */
	public static String getSuffix(){
		return preSuffix[1];
	}
	
	/**
	 * 根据请求Controller获取Controller的名称
	 * @param uri 去掉项目名的请求
	 * @return 长度为2的数组，第一个元素是urlPattern，第二个元素是Controller的className
	 */
	public static Object[] getControllerName(String uri){
		//遍历uri
		for(String url:urls){
			//找到指定的Controller
			if(uri.equals(url) || uri.startsWith(url+"/")){
				return new Object[]{url, controllerConfig.get(url)};
			}
		}
		//未查找到返回null
		return null;
	}
	
	/**
	 * 获取当前项目在配置下的所有Controller
	 */
	public static Map<String, Class<?>> getAllControllers(){
		return controllerConfig;
	}
	
	/**
	 * 获取urlSuffix配置
	 */
	public static String[] getUrlSuffixes(){
		return urlSuffixes;
	}
	
	/**
	 * 获取所有拦截器
	 */
	public static List<Class<? extends Interceptor>> getInterceptors(){
		return interceptors;
	}
	
	/**
	 * 获取Mapper映射
	 */
	public static Map<String, MapperEntity> getMapperMap(){
		return mapperMap;
	}
	
	/**
	 * 获取定时任务Bean映射
	 */
	public static Map<String, ScheduledTask> getScheduledBeanMap(){
		return scheduledBeanMap;
	}
	
	/**
	 * 获取所有配置
	 */
	public static Map<String, String> getConfig(){
		//如果尚未加载系统配置，则加载系统配置
		if(frameConfig==null){
			loadFrameConfig();
		}
		return frameConfig;
	}
	
	/**
	 * 扫描类
	 */
	private static void scanPackage() {
		//获取扫描地址
		String scanLocation = frameConfig.get("scanLocation");
		//存在配置
		if(StringUtils.isNotEmpty(scanLocation)) {
			//日志
			Logger.info("[{}] 开始扫描包。。。", InitialListener.getContextName());
			//开始时间
			long start = System.currentTimeMillis();
			//获取所有配置
			String[] locations = scanLocation.split(";");
			//循环解析
			for(String c: locations){
				loadPackageClass("/", root + c, c.replace('/', '.'));
			}
			//加载完成，处理后置操作
			dealInterceptor();
			dealWebsocket();
			//执行后的时间
			long end = System.currentTimeMillis();
			//计算执行时间
			Logger.info("[{}] 包扫描完毕，共消耗 {} ms", InitialListener.getContextName(), end-start);
		}
	}

	/**
	 * 加载根目录下的类
	 * @param relative 相对配置的位置
	 * @param rootpath 根目录
	 * @param preClass 类前缀
	 */
	@SuppressWarnings({"unchecked", "rawtypes"})
	private static void loadPackageClass(String relative, String rootpath, String preClass) {
		//创建File对象
		File file = new File(rootpath);
		//获取所有文件名
		Assert.doIfNotEmpty(file.list(), filenames->{
			//开始遍历文件
			for(String fname: filenames){
				//当前文件
				File f = new File(rootpath, fname);
				//如果是目录，则再次加载
				if(f.isDirectory()){
					//加载子目录
					loadPackageClass(relative + fname + "/", f.getAbsolutePath(), preClass + "." + fname);
				}else{
					//当前文件类型
					boolean controller = fname.endsWith("Controller.class"),
							interceptor = !controller && fname.endsWith("Interceptor.class"),
							websocket = !interceptor && fname.endsWith("WebSocket.class"),
							task = !websocket && fname.endsWith("ScheduledTask.class");
					//满足其中一种类型
					if(controller || interceptor || websocket || task) {
						//类型
						String type = controller ? "Controller" : (interceptor ? "Interceptor" : (websocket ? "WebSocket" : "ScheduledTask"));
						//获取类名
						String classname = fname.substring(0, fname.length() - type.length() - 6);
						//获取完整类名
						String fullClassName = preClass+"."+classname+type;
						try {
							//获取Class对象
							Class clazz = Class.forName(fullClassName);
							//控制器
							if(controller) {
								//获取key
								String key = relative + classname.substring(0, 1).toLowerCase() + classname.substring(1);
								//将Controller放入Map容器中
								controllerConfig.put(key, clazz);
								urls.add(key);
							}
							//如果是拦截器，则添加到集合
							else if(interceptor && Interceptor.class.isAssignableFrom(clazz)) {
								interceptors.add(clazz);
							}
							//如果是WebSocket，则添加到集合
							else if(websocket && WebSocket.class.isAssignableFrom(clazz)) {
								websockets.add(clazz);
							}
							//如果是ScheduledTask，则添加到集合
							else if(task && ScheduledTask.class.isAssignableFrom(clazz)) {
								scheduledBeanMap.put(clazz.getSimpleName(), (ScheduledTask) clazz.newInstance());
							}
						}catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
							//任务Bean
							if(task) {
								//创建任务失败，输出日志即可
								Logger.error("任务Bean创建失败：Bean名称：{}, 错误原因：{}", fullClassName, e.getMessage());
							}else {
								//输出异常，但不用处理
								Logger.error(e);
							}
						}
					}
				}
			}
		});
	}

	/**
	 * 加载urlSuffix配置
	 */
	private static void loadUrlSuffix(){
		//加载urlSuffix配置
		String urlSuffix = frameConfig.get("urlSuffix");
		if(!urlSuffix.isEmpty()){
			//获取所有后缀配置
			String[] suffixes = urlSuffix.split("\\|");
			//遍历加点
			for(int i=0,len=suffixes.length;i<len;i++){
				suffixes[i] = "." + suffixes[i];
			}
			//赋值
			urlSuffixes = suffixes;
		}else{
			urlSuffixes = new String[]{};
		}
	}
	
	/**
	 * 加载前后缀
	 */
	private static void loadPreSuffix(){
		//初始化变量
		preSuffix = new String[2];
		//获取前缀
		String prefix = frameConfig.get("dispatcherPrefix");
		String suffix = frameConfig.get("dispatcherSuffix");
		//检测是否为空
		if(StringUtils.isEmpty(prefix)){
			prefix = "/WEB-INF/content/";
		}
		if(StringUtils.isEmpty(suffix)){
			suffix = ".jsp";
		}
		//保存值
		preSuffix[0] = prefix;
		preSuffix[1] = suffix;
	}
	
	/**
	 * 加载Mapper配置
	 */
	private static void loadMapperFile() {
		//获取mapperResource配置
		String mapperResourceLocation = frameConfig.get("mapperResource");
		//存在
		if(StringUtils.isNotEmpty(mapperResourceLocation)) {
			Logger.info("[{}] 开始加载Mapper资源。。。", InitialListener.getContextName());
			//开始时间
			long start = System.currentTimeMillis();
			//分割mapperConfig
			String[] mappers = mapperResourceLocation.split(";");
			//遍历
			for(String mapper: mappers) {
				//开始加载Mapper资源
				MapperParser.loadAllMapper(root + mapper);
			}
			//检查未处理的SQL
			Parser.checkUnDealSQL();
			//执行后的时间
			long end = System.currentTimeMillis();
			//计算执行时间
			Logger.info("[{}] Mapper资源加载完毕，共消耗 {} ms", InitialListener.getContextName(), end-start);
		}
	}
	
	/**
	 * 类扫描完成，处理拦截器后置操作
	 */
	private static void dealInterceptor() {
		//拦截器个数
		int len = interceptors.size();
		//拦截器执行顺序数组
		String[] orders = new String[len];
		//获取拦截器排序
		for(int i=0;i<len;i++){
			//获取注解配置
			Interceptors iClass = interceptors.get(i).getAnnotation(Interceptors.class);
			//如果有注解，则获取索引
			if(iClass!=null){
				//临时值
				int index = iClass.order();
				//判断范围
				if(index>100 || index<0){
					index = 100;
				}
				orders[i] = String.format("%03d", index) + i;
			}else{
				//如果没有，默认为100
				orders[i] = "100"+i;
			}
		}
		//对索引排序
		Arrays.sort(orders);
		//临时集合
		List<Class<? extends Interceptor>> tempList = new ArrayList<>();
		//遍历索引数组
		for(String order: orders){
			tempList.add(interceptors.get(Integer.parseInt(order.substring(3))));
		}
		//重新赋值
		interceptors = tempList;
		//手动添加文件上传拦截器
		interceptors.add(0, UploadFileInterceptor.class);
		//依次初始化拦截器
		interceptors.forEach(interceptor->{
			//初始化拦截器
			try {
				Chain.interceptorChainList.add(interceptor.newInstance());
			}catch(InstantiationException | IllegalAccessException e) {
				throw new RuntimeException("拦截器实例化失败：" + interceptor.getName(), e);
			}
		});
	}
	
	/**
	 * 类扫描完成，处理WebSocket后置操作
	 */
	private static void dealWebsocket() {
		//有WebSocket
		if(!websockets.isEmpty()) {
			//遍历WebSocket
			for(Class<? extends WebSocket> ws: websockets) {
				//获取注解
				WebSocketBranch wsb = ws.getAnnotation(WebSocketBranch.class);
				//为空
				if(wsb==null) {
					throw new RuntimeException("自定义WebSocket："+ws.getName()+"没有添加WebSocketBranch注解");
				}
				try {
					WebSocketCore.addBranch(wsb.value(), FrameUtils.getResourceClass(ws, getResourceSingleton()));
				} catch (Exception e) {
					throw new RuntimeException("自定义WebSocket初始化失败", e);
				}
			}
		}
	}
}
